package com.devo.icecream.socket

import com.blankj.utilcode.util.DeviceUtils
import com.devo.icecream.AppLogger
import com.devo.icecream.IceCreamApp
import com.devo.icecream.data.BusinessTimeDto
import com.devo.icecream.data.SocketEntity
import com.google.gson.Gson
import com.xuhao.didi.core.iocore.interfaces.IPulseSendable
import com.xuhao.didi.core.iocore.interfaces.ISendable
import com.xuhao.didi.core.pojo.OriginalData
import com.xuhao.didi.socket.client.sdk.OkSocket
import com.xuhao.didi.socket.client.sdk.client.ConnectionInfo
import com.xuhao.didi.socket.client.sdk.client.OkSocketOptions
import com.xuhao.didi.socket.client.sdk.client.action.SocketActionAdapter
import com.xuhao.didi.socket.client.sdk.client.connection.IConnectionManager
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.debounce
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.filterIsInstance
import kotlin.concurrent.thread

@OptIn(FlowPreview::class)
object SocketClient {
    private const val HOST = "101.37.145.126"
    private const val PORT = 6687
    //socket传回来的机器码，第一次传空，之后有回复了更新值
    private var machineKeyCache = ""
    //心跳发送的实体类
    private val socketResultEntity by lazy {
        SocketEntity(
            //设备的位置
            gpsLocation = IceCreamApp.preferences.getString("location", "") ?: "",
            mac = DeviceUtils.getMacAddress().replace(":", "-"),
//            mac = "a0-9f-10-e8-23-24",//机器【Android的mac地址】
            plcCode = "45131351",//plc编码
            machineKey = machineKeyCache,//机器码
            content = "",
            command = 0
        )
    }

    var hasPlcError = false

    private var errorCount = 0;
    private const val errorThreshold = 3;

    private var workThread: Thread? = null
    private var connectionManager: IConnectionManager? = null
    private val gson = Gson()
    private val scope = CoroutineScope(Dispatchers.IO + Job())
    private val eventFlow = MutableSharedFlow<SocketEvent>()

    private val receiver = object : SocketActionAdapter() {
        override fun onSocketConnectionSuccess(info: ConnectionInfo?, action: String?) {
            super.onSocketConnectionSuccess(info, action)
            //连接成功
            AppLogger.w("Socket is connected")
            connectionManager?.pulseManager?.apply {
                pulseSendable = socketResultEntity
            }?.pulse()//开始心跳,开始心跳后,心跳管理器会自动进行心跳触发
        }

        override fun onSocketDisconnection(info: ConnectionInfo?, action: String?, e: Exception?) {
            super.onSocketDisconnection(info, action, e)
            AppLogger.w("Socket is disconnected")
            eventFlow.postEvent(DisconnectEvent)
        }

        override fun onSocketConnectionFailed(
            info: ConnectionInfo?,
            action: String?,
            e: java.lang.Exception?
        ) {
            super.onSocketConnectionFailed(info, action, e)
            AppLogger.w("Socket is Failed")
        }

        override fun onSocketIOThreadStart(action: String?) {

            AppLogger.w("onSocketIOThreadStart")
            super.onSocketIOThreadStart(action)
        }

        override fun onSocketIOThreadShutdown(action: String?, e: java.lang.Exception?) {
            AppLogger.w("onSocketIOThreadShutdown")
            super.onSocketIOThreadShutdown(action, e)
        }

        override fun onSocketWriteResponse(
            info: ConnectionInfo?,
            action: String?,
            data: ISendable?
        ) {
            AppLogger.w("onSocketWriteResponse")
            super.onSocketWriteResponse(info, action, data)
        }

        override fun onPulseSend(info: ConnectionInfo?, data: IPulseSendable?) {
            super.onPulseSend(info, data)
            AppLogger.json(gson.toJson(data))
        }

        override fun onSocketReadResponse(
            info: ConnectionInfo?,
            action: String?,
            data: OriginalData?
        ) {
            super.onSocketReadResponse(info, action, data)
            //接收到心跳
            val entity = data?.run {
                val str = String(bodyBytes)
                AppLogger.json(str)
                gson.fromJson(str, SocketEntity::class.java)
            }
            entity?.let {
                disposeEvent(it)
            }
            //动态发消息给socket
            connectionManager?.pulseManager?.let {
                it.feed() //喂狗操作
                val socketEntity = it.pulseSendable as SocketEntity
                it.pulseSendable = socketEntity.copy(
                    content = IceCreamApp.preferences.getString("content", "") ?: "",//application里面动态获取
                    machineKey = machineKeyCache,
                    gpsLocation = IceCreamApp.preferences.getString("location", "") ?: ""
                )
            }
        }
    }

    private fun disposeEvent(entity: SocketEntity) {

        if (entity.data is Map<*, *> && entity.data.containsKey("status")) {
            val status = entity.data["status"] as? Double
            if (status != null) {
                AppLogger.e("当前Status - $status")
                eventFlow.postEvent(UpdatedStatus(status.toInt()))
            }
        }

        when (entity.type) {
            "connected", "businessTime" -> {
                if (entity.data is Map<*, *>
                    && entity.data.containsKey("start")
                    && entity.data.containsKey("end")
                ) {
                    val data = gson.fromJson(gson.toJson(entity.data), BusinessTimeDto::class.java)
                    eventFlow.postEvent(BusinessTimeChanged(data))
                }
                //socket返回的机器码
                machineKeyCache = entity.machineKey
                eventFlow.postEvent(MachineKeyUpdated(machineKeyCache))
            }
            "orderPaid" -> {
                eventFlow.postEvent(OrderPaid(entity.data.toString()))
            }
            "updateStatus" -> {
                eventFlow.postEvent(
                    UpdatedStatus(
                        entity.data.toString().toInt()
                    )
                )
            }
            else -> {
                AppLogger.e("unsupported type: ${entity.type}/${entity.message}")
            }
        }

    }

    fun init() {
        if (workThread != null) {
            return
        }
        workThread = thread(start = true, name = "socket-thread") {
            connectionManager = OkSocket.open(ConnectionInfo(HOST, PORT))
            connectionManager?.apply {
                option(
                    OkSocketOptions.Builder(option)
                        .setConnectTimeoutSecond(60)
                        .setPulseFrequency(5 * 1000)
                        .setConnectionHolden(true)
                        .build()
                )
                registerReceiver(receiver)
            }?.connect()
        }
    }

    fun getMachineKeyChangeFlow() =
        eventFlow.filterIsInstance<MachineKeyUpdated>()
            .distinctUntilChanged { a, b -> a.key == b.key }

    fun getBusinessTimeChangedFlow() =
        eventFlow.filterIsInstance<BusinessTimeChanged>()

    fun getUpdatedStatusFlow() =
        eventFlow.filterIsInstance<UpdatedStatus>()
            .debounce(3000)
            .distinctUntilChanged { old, new -> old.status == new.status }

    fun getOrderPaidFlow() = eventFlow.filterIsInstance<OrderPaid>().distinctUntilChanged()
    fun getDisconnectFlow() = eventFlow.filterIsInstance<DisconnectEvent>().distinctUntilChanged()

    fun destroy() {
        connectionManager?.disconnect()
        workThread?.interrupt()
    }

    private fun <T> MutableSharedFlow<T>.postEvent(value: T) {
        scope.launch {
            emit(value)
        }
    }

}